<template>
  <div ref="root" v-bind="html_binds">
    <div ref="options" class="w-slidepicker-options">
      <slot />
    </div>
    <div class="w-slidepicker-bg-top"></div>
    <div class="w-slidepicker-bg-bottom"></div>
  </div>
</template>

<script>
  import $ from '../dfish.js'
  import WidgetBase from './WidgetBase.vue'
  import {
    getCurrentInstance,
    onMounted,
    watch,
    computed,
  } from 'vue'
  import ResizeObserver from 'resize-observer-polyfill'

  export default {
    name: 'w-slidepicker',
    mixins: [WidgetBase],
    props: ['optionHeight', 'modelValue', 'minValue', 'maxValue', 'keepIndex'],
    emits: ['update:modelValue', 'change', 'scroll:top', 'scroll:bottom'],
    setup() {
      const self = getCurrentInstance().ctx
      let startY, endY, minY, maxY, optY, optH, roll

      const mousedown = function(downEv) {
        let downY = $.eventY(downEv)
        let moveY, lastY, lastTime
        let speed = 0
        let lastSpeed = null
        self.$refs.options.style.transition = ''
        $.moveup(e => {
          let nowY = $.eventY(e)
          let nowTime = e.timeStamp
          moveY = optY + nowY - downY
          if (lastY && lastTime && nowTime > lastTime) {
            lastSpeed = speed
            speed = (nowY - lastY) / (nowTime - lastTime)
          }
          lastY = nowY
          lastTime = nowTime
          moveY = $.numRange(moveY, minY - optH, maxY + optH)
          self.$refs.options.style.transform = `translateY(${moveY}px)`
        }, e => {
          if (moveY == null) return
          let minSpeed = 0.15 //speed大于这个值时，做惯性滚动
          if (Math.abs(speed) < minSpeed || (lastSpeed && Math.abs(lastSpeed) < minSpeed)) {
            speed = 0
          } else if (lastSpeed && Math.abs(lastSpeed) > Math.abs(speed)) {
            speed = lastSpeed //记录最后两个speed，选择其中一个最大的，此步骤可优化体验
          }
          let y = moveY + (speed * 200)
          let s = Math.abs(speed / 2)
          let yuH = (maxY - y) % optH
          y += yuH
          if (yuH > (optH / 2)) {
            y -= optH
          }
          optY = $.numRange(y, minY, maxY)
          if (optY != moveY) {
            if (moveY < minY || moveY > minY || s < minSpeed) {
              s = minSpeed
            }
            if (Math.abs(optY - moveY) < 10) {
              s = 0.05 // 移动范围很小时，过渡时间也相应缩短
            }
            self.$refs.options.style.transform = `translateY(${optY}px)`
            self.$refs.options.style.transition = `transform ${s}s ease-out`
          }
        })
      }
      const focus = function() {
        if (!self.$refs.root || !self.$refs.options) return
        let sc = self.slots_children()
        if (!sc.length) return
        let v = self.modelValue
        let nv = self.minValue
        let xv = self.maxValue
        if (nv || nv === 0 || xv || xv === 0) {
          v = $.numRange(v, nv, xv)
          if (v != self.modelValue) {
            update(v)
          }
        }
        let bcrRoot = $.bcr(self.$refs.root)
        let bcrOpt = $.bcr(self.$refs.options)
        let ni = -1
        let xi = -1
        let asc = true
        optH = self.x.optionHeight
        startY = maxY = (bcrRoot.height - optH) / 2
        endY = minY = startY - bcrOpt.height + optH
        if (nv || nv === 0) {
          ni = sc.findIndex(s => s.props.value == nv)
        }
        if (xv || xv === 0) {
          xi = sc.findIndex(s => s.props.value == xv)
        }
        if ((ni > -1 || xi > -1) && sc[0].props.value > sc[sc.length - 1].props.value) { // 判断是不是倒序
          asc = false
        }
        if (ni > -1) {
          if (asc) {
            maxY -= ni * optH
          } else {
            minY += ni * optH
          }
        }
        if (xi > -1) {
          if (asc) {
            minY += (sc.length - 1 - xi) * optH
          } else {
            maxY -= (sc.length - 1 - xi) * optH
          }
        }
        if (v != null) {
          let index = sc.findIndex(s => s.props.value == v)
          if (index === -1) {
            update(optY != null && optY < endY && self.keepIndex ? sc[sc.length - 1].props.value : sc[0].props.value)
          } else {
            optY = startY - (optH * index)
          }
        } else {
          optY = startY
        }
        optY = $.numRange(optY, minY, maxY)
        self.$refs.options.style.transition = ''
        self.$refs.options.style.transform = `translateY(${optY}px)`
      }

      const update = function(v) {
        roll = true
        self.$emit('update:modelValue', v)
        self.$emit('change', v)
      }

      const transEnd = function() {
        let index = (startY - optY) / optH
        let ret = self.slots_children()[index]
        ret && update(ret.props.value)
      }
      
      const x_default = function() {
        return {
          optionHeight: 32
        }
      }

      const html_props = computed(() => {
        return {
          style: {
            '--w-option-height': $.unitSize(self.x.optionHeight)
          },
          [$.br.mobile ? 'ontouchstart' : 'onmousedown']: mousedown
        }
      })
      
      watch(() => self.modelValue, (v) => {
        if (!roll) focus()
        roll = false
      })
      watch(() => self.minValue, focus)
      watch(() => self.maxValue, focus)

      onMounted(() => {
        new ResizeObserver(() => {
          self.$refs.options && focus()
        }).observe(self.$refs.options)
        self.$refs.options.addEventListener('transitionend', transEnd)
      })

      return {
        x_default,
        mousedown,
        html_props,
      }
    }
  }
</script>

<style>
  .w-slidepicker {
    position: relative;
    overflow: hidden;
    background: #fff;
  }

  .w-slidepicker-bg-top {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: calc(50% - var(--w-option-height, 32px)/2);
    background: linear-gradient(rgba(245, 245, 245, 0.9), rgba(245, 245, 245, 0.4));
    border-bottom: 1px solid #ddd;
    pointer-events: none;
    box-sizing: border-box;
  }

  .w-slidepicker-bg-bottom {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: calc(50% - var(--w-option-height, 32px)/2);
    background: linear-gradient(rgba(245, 245, 245, 0.4), rgba(245, 245, 245, 0.9));
    border-top: 1px solid #ddd;
    pointer-events: none;
    box-sizing: border-box;
  }

  .w-slidepicker .w-option {
    width: 100%;
    text-align: center;
  }

  .w-slidepicker .w-option-checked {
    display: none;
  }

  .w-slidepicker-options {
    user-select: none;
    pointer-events: none;
  }
</style>
